package com.xingchi.tornado.generator.execute;

import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.Version;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.INameConvert;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
import com.baomidou.mybatisplus.generator.config.builder.CustomFile;
import com.baomidou.mybatisplus.generator.config.builder.Entity;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.IColumnType;
import com.xingchi.tornado.generator.config.DTOConfig;
import com.xingchi.tornado.generator.config.QueryConfig;
import com.xingchi.tornado.generator.config.VOConfig;
import com.xingchi.tornado.generator.engine.EnhanceVMTemplateEngine;
import com.xingchi.tornado.model.BaseDO;
import com.xingchi.tornado.model.BaseDTO;
import com.xingchi.tornado.model.PaginationQuery;
import com.xingchi.tornado.mybatisplus.service.AbstractBasePlusService;
import com.xingchi.tornado.mybatisplus.service.BasePlusService;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Field;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author xingchi
 * @date 2024/3/31 12:18
 */
public class AutoGenerator {

    public static void main(String[] args) {
        FastAutoGenerator fastAutoGenerator = FastAutoGenerator.create("jdbc:mysql://116.62.56.202:53308/tornado_framework?useSSL=false&serverTimezone=UTC", "root", "xy19981123..")
                .globalConfig(builder -> {
                    builder.author("xingchi") // 设置作者
                            .enableSpringdoc() // 开启 swagger 模式
                            .disableOpenDir() //禁止打开输出目录
                            .commentDate("yyyy-MM-dd HH:mm")
                            .outputDir("D://" + "src/main/java"); // 指定输出目录
                })
                .dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
                    int typeCode = metaInfo.getJdbcType().TYPE_CODE;
                    if (typeCode == Types.TINYINT || typeCode == Types.BIT) {
                        // 自定义类型转换
                        return DbColumnType.INTEGER;
                    }
                    return typeRegistry.getColumnType(metaInfo);

                }))
                .packageConfig(builder -> {
                    builder.parent("com.xingchi.tornado") // 设置父包名
                            .moduleName("test") // 设置父包模块名
                            .entity("model.domain") //设置entity包名
                            .pathInfo(Collections.singletonMap(OutputFile.xml, "D://" + "src/main/resources/mapper")); // 设置mapperXml生成路径

                })
                .strategyConfig(builder -> {
                    builder.addInclude("user"); // 设置需要生成的表名
                    builder.entityBuilder()
                            .superClass(BaseDO.class)
                            .enableTableFieldAnnotation()
                            .enableLombok()
                            .convertFileName(entityName -> entityName + "DO")
                            .addSuperEntityColumns("id", "create_time", "update_time", "create_by", "update_by", "deleted");
                    builder.controllerBuilder()
                            .enableRestStyle()
                            .enableHyphenStyle()
                            .convertFileName(entityName -> entityName + "Controller");
                    builder.serviceBuilder()
                            .convertServiceFileName(entityName -> entityName + "Service")
                            .superServiceClass(BasePlusService.class)
                            .convertServiceImplFileName(entityName -> entityName + "ServiceImpl")
                            .superServiceImplClass(AbstractBasePlusService.class);
                    builder.mapperBuilder()
                            .superClass(BaseMapper.class)
                            .convertXmlFileName(entityName -> entityName + "Mapper")
                            .convertMapperFileName(entityName -> entityName + "Mapper");
                })
                .injectionConfig(consumer -> {
                    Map<String, Object> customMap = new HashMap<>();
                    // dto
                    DTOConfig dtoConfig = DTOConfig.builder()
                            .templatePath("templates/dto.java.vm")
                            .packageName("model.dto")
                            .superClass(BaseDTO.class)
                            .converterFileName(source -> source + "DTO")
                            .build();
                    customMap.put("dtoConfig", dtoConfig);

                    // vo
                    VOConfig voConfig = VOConfig.builder()
                            .templatePath("templates/vo.java.vm")
                            .packageName("model.vo")
                            .converterFileName(source -> source + "VO")
                            .build();
                    customMap.put("voConfig", voConfig);

                    // query
                    QueryConfig queryConfig = QueryConfig.builder()
                            .templatePath("templates/query.java.vm")
                            .packageName("model.dto")
                            .superClass(PaginationQuery.class)
                            .converterFileName(source -> source + "QueryDTO")
                            .build();
                    customMap.put("queryConfig", queryConfig);

                    consumer.beforeOutputFile((tableInfo, objectMap) -> {
                        consumer.build().getCustomFiles().clear();
                        // 获取配置信息
                        ConfigBuilder config = (ConfigBuilder) objectMap.get("config");
                        StrategyConfig strategyConfig = config.getStrategyConfig();

                        // 原始的实名名称
                        INameConvert.DefaultNameConvert defaultNameConvert = new INameConvert.DefaultNameConvert(strategyConfig);
                        String entityName = defaultNameConvert.entityNameConvert(tableInfo);
                        String camelToHyphen = com.baomidou.mybatisplus.core.toolkit.StringUtils.camelToHyphen(entityName);
                        objectMap.put("simpleEntity", entityName);
                        objectMap.put("simpleCamelToHyphen", camelToHyphen);

                        List<TableField> allField = getAllFields(tableInfo, strategyConfig);

                        // dto配置
                        CustomFile dtoCustomFile = getDTOCustomFile(config, objectMap, entityName, allField);
                        consumer.customFile(dtoCustomFile);

                        // vo配置
                        CustomFile voCustomFile = getVOCustomFile(config, objectMap, entityName, allField);
                        consumer.customFile(voCustomFile);

                        // query配置
                        CustomFile queryCustomFile = getQueryCustomFile(config, objectMap, entityName, allField);
                        consumer.customFile(queryCustomFile);
                    });
                    consumer.customMap(customMap);

                });
        fastAutoGenerator.templateEngine(new EnhanceVMTemplateEngine());
        fastAutoGenerator.execute();

    }

    private static CustomFile getQueryCustomFile(ConfigBuilder config, Map<String, Object> objectMap, String entityName, List<TableField> allField) {
        QueryConfig queryConfig = (QueryConfig) objectMap.get("queryConfig");
        Class<?> superClass = queryConfig.getSuperClass();
        if (superClass != null) {
            objectMap.put("querySuperClass", superClass.getSimpleName());
            objectMap.put("querySuperClassName", superClass.getName());
        }

        objectMap.put("queryName", queryConfig.getConverterFileName().convert(entityName));

        return queryConfig.render(entityName);
    }

    private static CustomFile getVOCustomFile(ConfigBuilder config, Map<String, Object> objectMap, String entityName, List<TableField> allField) {
        VOConfig voConfig = (VOConfig) objectMap.get("voConfig");
        Class<?> superClass = voConfig.getSuperClass();

        List<TableField> voFields = getTableFields(allField, superClass);
        Set<String> pkgs = voFields.stream()
                .map(TableField::getColumnType)
                .map(IColumnType::getPkg)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toSet());
        // 添加父类导包名
        if (superClass != null) {
            pkgs.add(superClass.getName());
            // 添加vo父类名称
            objectMap.put("voSuperClass", superClass.getSimpleName());
        }


        // 扩充dto中的参数
        objectMap.put("VO", voConfig.getPackageName());
        objectMap.put("voPkgs", pkgs);
        objectMap.put("voFields", voFields);
        objectMap.put("voName", voConfig.getConverterFileName().convert(entityName));

        return voConfig.render(entityName);
    }

    private static CustomFile getDTOCustomFile(ConfigBuilder config, Map<String, Object> objectMap, String entityName, List<TableField> allField) {
        DTOConfig dtoConfig = (DTOConfig) objectMap.get("dtoConfig");
        Class<?> superClass = dtoConfig.getSuperClass();

        List<TableField> dtoFields = getTableFields(allField, superClass);

        Set<String> pkgs = dtoFields.stream()
                .map(TableField::getColumnType)
                .map(IColumnType::getPkg)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toSet());

        // 添加父类导包名
        if (superClass != null) {
            pkgs.add(superClass.getName());
            // 添加dto父类名称
            objectMap.put("dtoSuperClass", superClass.getSimpleName());
        }


        // 扩充dto中的参数
        objectMap.put("DTO", dtoConfig.getPackageName());
        objectMap.put("dtoPkgs", pkgs);
        objectMap.put("dtoFields", dtoFields);
        objectMap.put("dtoName", dtoConfig.getConverterFileName().convert(entityName));

        // 生成DTO
        return dtoConfig.render(entityName);
    }

    @NotNull
    private static List<TableField> getTableFields(List<TableField> allField, Class<?> superClass) {
        Set<String> superClassFieldName = new HashSet<>();
        if (superClass != null) {
            Field[] superClassFields = superClass.getDeclaredFields();
            superClassFieldName.addAll(Arrays.stream(superClassFields).map(Field::getName).collect(Collectors.toSet()));
        }

        // 剔除掉父类中包含的字段
        return allField.stream().filter(item -> !superClassFieldName.contains(item.getPropertyName())).collect(Collectors.toList());
    }

    /**
     * 获取有效的字段，不包含逻辑删除以及版本号字段
     *
     * @param tableInfo      表信息
     * @param strategyConfig 策略配置用于获取实体
     * @return 有效的字段
     */
    @NotNull
    private static List<TableField> getAllFields(TableInfo tableInfo, StrategyConfig strategyConfig) {
        List<TableField> fields = tableInfo.getFields();
        List<TableField> commonFields = tableInfo.getCommonFields();

        // 计算dto需要导的包以及字段
        // 先拿到所有字段，在移除DTO父类中包含的字段
        List<TableField> allField = new ArrayList<>();
        allField.addAll(fields);
        allField.addAll(commonFields);
        TableField primaryKey = null;
        Iterator<TableField> iterator = allField.iterator();
        while (iterator.hasNext()) {
            TableField next = iterator.next();
            if (next.isKeyFlag()) {
                primaryKey = next;
                iterator.remove();
                break;
            }
        }
        if (primaryKey != null) {
            allField.add(0, primaryKey);
        }

        Entity entity = strategyConfig.entity();
        String logicDeletePropertyName = entity.getLogicDeletePropertyName();
        String versionPropertyName = entity.getVersionPropertyName();

        Set<String> removeFieldNames = new HashSet<>();
        removeFieldNames.add(logicDeletePropertyName);
        removeFieldNames.add(versionPropertyName);

        String superClass = entity.getSuperClass();
        if (StringUtils.isNotBlank(superClass)) {
            Class<?> aClass = null;
            try {
                aClass = Class.forName(superClass);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            if (aClass != null) {
                Field[] declaredFields = aClass.getDeclaredFields();
                List<String> fieldNames = Arrays.stream(declaredFields).filter(item -> item.getAnnotation(TableLogic.class) != null || item.getAnnotation(Version.class) != null).map(Field::getName).collect(Collectors.toList());
                removeFieldNames.addAll(fieldNames);
            }
        }

        // 逻辑删除字段 和 版本号字段 不在DTO和VO中展示
        allField.removeIf(item -> removeFieldNames.contains(item.getPropertyName()));
        return allField;
    }

}
